﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using RayDen.Library.Components.SystemComponents;
using RayDen.RayEngine.Core;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;
using RayDen.RayEngine.Samplers;

namespace RayDen.RayEngine
{
    public abstract class MultiPathBufferBase : PathBufferBase
    {
        protected new List<PathSamplerBase>[] paths;

        protected List<PathSamplerBase> CurrentPaths
        {
            get { return this.paths[GetCurrentMode()]; }
        }

        protected MultiPathBufferBase()
        {
            volumePaths = false;
        }

        protected abstract int GetCurrentMode();

        protected override PathSamplerBase CreatePathSampler()
        {
           throw new NotImplementedException();
        }

        public override void Init(int maxPaths, RayEngineScene scene, Core.Interface.IImageFilm agg, SamplingContext sc)
        {
            this.maxPaths = maxPaths;
            this.surfaceSampler = (SurfaceSampler)sc.SurfaceSampler;
            this.Sampler = sc.PrimarySpaceSampler;
            this.Scene = scene;
            this.film = agg;
            this.sampleBuffer = film.GetFreeSampleBuffer();
            this.paths = new []
                {
                    new List<PathSamplerBase>(scene.MaxPaths + 1),
                    new List<PathSamplerBase>(scene.MaxPaths + 1),
                };
        }

        internal abstract bool IsModeSwitch();
        internal abstract void OnModeSwitch();
        internal virtual void Splat(SampleBuffer buffer)
        {
            if (this.sampleBuffer.IsFull())
            {
                this.statsTotalSampleCount += this.sampleBuffer.GetSampleCount();
                // Splat all samples on the film
                this.film.SplatSampleBuffer(true, sampleBuffer);
                this.sampleBuffer = this.film.GetFreeSampleBuffer();
            }
        }

        public override void ReInit()
        {
            foreach (var pathCollection in paths)
            {
                for (var i = 0; i < pathCollection.Count; ++i)
                    pathCollection[i].InitPath(this);
            }

            firstPath = 0;
            film.ResetProcessor();

            statsRenderingStart = DateTime.UtcNow.Ticks / 1000;
            statsTotalSampleCount = 0;
        }

        public new void ClearPaths()
        {
            CurrentPaths.Clear();
        }

        public override void FillRayBuffer( RayBuffer rayBuffer )
        {
            if (CurrentPaths.Count == 0)
            {
                // Need at least 2 paths
                CurrentPaths.Add(this.CreatePathSampler());
                CurrentPaths.Add(this.CreatePathSampler());
                CurrentPaths[0].InitPath(this);
                CurrentPaths[1].InitPath(this);
                firstPath = 0;
            }

            bool allPathDone = true;
            lastPath = firstPath;
            for (; ; )
            {
                if (!CurrentPaths[lastPath].FillRayBuffer(rayBuffer))
                {
                    // Not enough free space in the RayBuffer
                    allPathDone = false;
                    break;
                }

                lastPath = (lastPath + 1) % CurrentPaths.Count;
                if (lastPath == firstPath)
                    break;
            }

            if (allPathDone)
            {
                var newPaths = 0;
                var maxNewPaths = maxPaths;

                for (; ; )
                {
                    newPaths++;
                    if (newPaths > maxNewPaths)
                    {
                        firstPath = 0;
                        lastPath = CurrentPaths.Count - 1;
                        break;
                    }

                    var p = this.CreatePathSampler();
                    CurrentPaths.Add(p);
                    p.InitPath(this);
                    if (!p.FillRayBuffer(rayBuffer))
                    {
                        firstPath = 0;
                        lastPath = CurrentPaths.Count - 2;
                        break;
                    }
                }
            }
        }

        public override void AdvancePaths( RayBuffer rayBuffer )
        {
#if VERBOSE
            try
            {
            if (CurrentPaths.Count == 0)
            {
                // Need at least 2 paths
                CurrentPaths.Add(this.CreatePathSampler());
                CurrentPaths.Add(this.CreatePathSampler());
                CurrentPaths[0].InitPath(this);
                CurrentPaths[1].InitPath(this);
                firstPath = 0;
            }
#endif

            for (int i = firstPath; i != lastPath; i = (i + 1) % CurrentPaths.Count)
            {
                CurrentPaths[i].Advance(rayBuffer, this.sampleBuffer);

                if (this.IsModeSwitch())
                {
                    this.OnModeSwitch();
                    firstPath = 0;
                    return;
                }

                Splat(sampleBuffer);
            }

            firstPath = (lastPath + 1) % CurrentPaths.Count;

#if VERBOSE
            }
            catch (Exception ex)
            {
                Tracer.TraceLine(ex.Message);
                Tracer.TraceLine(ex.StackTrace);
                throw;
            }
#endif
        }

        protected override void OnSplat(PathSamplerBase item)
        {
            
        }

        public new IEnumerator<PathSamplerBase> GetEnumerator()
        {
            return this.CurrentPaths.GetEnumerator();
        }
    }

}